package com.mini.mybatis.mybatisministep05.binding;

import com.mini.mybatis.mybatisministep05.sqlsession.SqlSession;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

/**
 *  1.当我们来设计一个ORM 框架的过程中，首先要考虑怎么把用户定义的数据库操作接口、xml配置的SQL语句、数据库三者联系起来。
 *      其实最适合的操作就是  使用代理的方式  进行处理，因为代理可以封装一个复杂的流程为接口对象的实现类
 *  2.MapperProxy 负责实现 InvocationHandler 接口的 invoke 方法，最终所有的实际调用都会调用到这个方法包装的逻辑。
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

    private SqlSession sqlSession;

    // final修饰的属性只能被初始化赋值一次
    private final Class<T> mapperInterface;

    private final Map<Method, MapperMethod> methodCache;


    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
        this.methodCache = methodCache;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (Object.class.equals(method.getDeclaringClass())){
            return method.invoke(this,args);
        } else {
            // 从缓存中获取，享元模式，不然每次新的请求都会创建MapperMethod对象
            MapperMethod mapperMethod = cachedMethod(method);
            // 真正执行通过mapperMethod
            return mapperMethod.execute(sqlSession,args);
        }
    }

    private MapperMethod cachedMethod(Method method) {
        // 先从缓存中获取方法
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null){
            // 缓存中没有再new
            mapperMethod = new MapperMethod(sqlSession.getConfiguration(), mapperInterface, method);
            methodCache.put(method,mapperMethod);
        }
        return mapperMethod;
    }
}
